home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mush-7.1.1 / file.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-05-02  |  14.0 KB  |  514 lines

  1. /* file.c -- Copyright (1988) Dan Heller */
  2.  
  3. #include "mush.h"
  4. #include <pwd.h>
  5.  
  6. /* takes string 'p' and address of int (isdir).  If p uses the ~ to reference
  7.  * a home directory of some sort, then expand it.  find out what sort of
  8.  * file final path is. set isdir to 1 if a directory, 0 if not, -1 on error
  9.  * return final path. If an error occurs, return string indicating error.
  10.  * if isdir has a value of 1 when passed, it ignores "No such file or directory"
  11.  */
  12. char *
  13. getpath(p, isdir)
  14. register char *p;
  15. int *isdir;
  16. {
  17.     static char buf[MAXPATHLEN];
  18.     struct stat stat_buf;
  19.  
  20.     if (!p || !*p || !strcmp(p, "~")) {
  21.     char *home = do_set(set_options, "home");
  22.     if (!home || !*home)
  23.         home = ALTERNATE_HOME;
  24.     (void) strcpy(buf, home);  /* no arg means home */
  25.     } else if (*p == '~') {
  26.     if (p[1] != '/') {
  27.         /* not our home, but someone else's
  28.          * look for ~user or ~user/subpath
  29.          * if '/' exists, separate into tmp="user" p="subpath"
  30.          */
  31.         struct passwd *ent, *getpwnam();
  32.         char *p2 = p+1;
  33.         if (p = index(p2, '/'))
  34.         *p++ = 0;
  35.         if (!(ent = getpwnam(p2))) {
  36.         *isdir = -1;
  37.         return sprintf(buf, "no such user: %s", p2);
  38.         }
  39.         /* append subpath to pathname */
  40.         if (p && *p)
  41.         (void) sprintf(buf, "%s/%s", ent->pw_dir, p);
  42.         /* if *p == NULL, pathname is done (buf), set isdir = 1 */
  43.         else {
  44.         *isdir = 1;
  45.         return strcpy(buf, ent->pw_dir);
  46.         }
  47.     } else {
  48.         char *home = do_set(set_options, "home");
  49.         if (!home || !*home)
  50.         home = ALTERNATE_HOME;
  51.         (void) sprintf(buf, "%s/%s", home, p+2);
  52.     }
  53.     } else if (*p == '%') {
  54.     /* if %user, append user name... else, it's just us */
  55.     if (!*++p || *p == ' ' || *p == '\t')
  56.         (void) strcpy(buf, spoolfile);
  57.     else
  58. #ifndef HOMEMAIL
  59.         (void) sprintf(buf, "%s/%s", MAILDIR, p);
  60. #else /* HOMEMAIL */
  61.     {
  62.         /* If it's NOT us, recur to get the path for ~user/MAILFILE */
  63.         int t_isdir = *isdir;
  64.         char *t, tmp[MAXPATHLEN];
  65.         (void) sprintf(tmp, "~%s/%s", p, MAILFILE);
  66.         t = getpath(tmp, &t_isdir);
  67.         if (t_isdir == -1) {
  68.         *isdir = -1;
  69.         return t;
  70.         }
  71.         /* strcpy(buf, t); --buf already has info because it's static */
  72.     }
  73. #endif /* HOMEMAIL */
  74.     } else if (*p == '+') {
  75.     register char *p2 = do_set(set_options, "folder");
  76.     if (!p2 || !*p2)
  77.         p2 = DEF_FOLDER;
  78.     if (*++p)
  79.         (void) sprintf(buf, "%s/%s", p2, p);
  80.     else
  81.         (void) strcpy(buf, p2);
  82.     if (*buf != '/') {
  83.         int t_isdir = *isdir;
  84.         char *t, tmp[MAXPATHLEN];
  85.         if (*buf != '~')
  86.         (void) sprintf(tmp, "~/%s", buf);
  87.         else
  88.         (void) strcpy(tmp, buf);
  89.         t = getpath(tmp, &t_isdir);
  90.         if (t_isdir == -1) {
  91.         *isdir = -1;
  92.         return t;
  93.         }
  94.         /* strcpy(buf, t); --buf already has info because it's static */
  95.     }
  96.     } else {  /* allow \ to escape the special chars, +, %, ~ */
  97.     if (*p == '\\')
  98.         p++;
  99.     (void) strcpy(buf, p);
  100.     }
  101.     if (stat(buf, &stat_buf)) {
  102.     (void) access(buf, F_OK); /* set errno to the "real" reason */
  103.     if (errno == ENOENT && *isdir == 1) {
  104.         *isdir = 0; /* say it's a regular file even tho it doesn't exist */
  105.         return buf; /* it may be wanted for creating */
  106.     }
  107.     *isdir = -1;
  108.     return sys_errlist[errno];
  109.     }
  110.     *isdir = ((stat_buf.st_mode & S_IFMT) == S_IFDIR);
  111.     return buf;
  112. }
  113.  
  114. /*
  115.  * Given a (possibly NULL or empty) string, return the name of a a valid
  116.  * directory.  The string may contain the usual filename metachars (see
  117.  * above).  Returns the current user's home directory if the input string
  118.  * does not refer to a directory, the ALTERNATE_HOME if the user's home
  119.  * directory cannot be found, or NULL if none of the above are accessible.
  120.  *
  121.  * NOTE:  Returns the getpath() static buffer, so the same caveats apply.
  122.  */
  123. char *
  124. getdir(path)
  125. char *path;
  126. {
  127.     int isdir = 0;
  128.  
  129.     /* getpath() already handles the NULL and empty cases */
  130.     if (!(path = getpath(path, &isdir)) || isdir != 1) {
  131.     isdir = 0;
  132.     path = getpath(ALTERNATE_HOME, &isdir);
  133.     if (isdir != 1)
  134.         path = NULL;
  135.     }
  136.     return path;
  137. }
  138.  
  139. /*
  140.  * Given a filename[pointer] (p), a file pointer, and a mode, file_to_fp
  141.  * opens the file with the mode.
  142.  * If the mode is "r" then we read the file into the file pointer at the
  143.  * end (fseek(fp, 2, 0)).  If the file is opened for writing, then read
  144.  * from the beginning of fp and write it into the file.
  145.  * This is usually called to read .signatures into messages (thus,
  146.  * opening .signature with "r" and writing to the end of fp which is probably
  147.  * the sendmail process or the message file pointer) or to write fortunes into
  148.  * the message buffer: reading fp (the popened fortune) and writing into file.
  149.  */
  150. file_to_fp(p, fp, mode)
  151. register char *p;
  152. register FILE *fp;
  153. char *mode;
  154. {
  155.     int     x = 1;
  156.     char     *file, buf[BUFSIZ];
  157.     FILE     *tmp_fp;
  158.  
  159.     if (!p || !*p) {
  160.     print("specify filename");
  161.     return -1;
  162.     }
  163.     /* Special case for IS_SENDING && !IS_GETTING should eventually go away */
  164.     if (ison(glob_flags, IS_SENDING) && isoff(glob_flags, IS_GETTING) &&
  165.         strcmp(p, "-") == 0) {
  166.     file = p;
  167.     if (*mode == 'r')
  168.         tmp_fp = stdin;
  169.     else
  170.         tmp_fp = stdout;
  171.     } else {
  172.     file = getpath(p, &x);
  173.     if (x == -1) { /* on error, file contains error message */
  174.         wprint(file);
  175.         return -1;
  176.     }
  177.     wprint("%s: ", file);
  178.     if (x) {
  179.         /* if x == 1, then path is a directory */
  180.         wprint("is a directory.\n");
  181.         return -1;
  182.     } else if (!(tmp_fp = fopen(file, mode))) {
  183.         wprint("%s\n", sys_errlist[errno]);
  184.         return -1;
  185.     }
  186.     }
  187.     if (*mode != 'r') {
  188.     rewind(fp);
  189.     for(x = 0; fgets(buf, BUFSIZ, fp); x++)
  190.         (void) fputs(buf, tmp_fp);
  191.     } else {
  192.     for(x = 0; fgets(buf, BUFSIZ, tmp_fp); x++)
  193.         (void) fputs(buf, fp);
  194.     (void) fflush(fp);
  195.     }
  196.     wprint("%s%d line%s\n", (*mode == 'a')? "added ": "",
  197.                   x, (x == 1)? "": "s");
  198.     if (file != p || strcmp(file, "-") != 0)
  199.     (void) fclose(tmp_fp);
  200.     return 0;
  201. }
  202.  
  203. /* clear all contents of the file.  Careful that the file is opened for
  204.  * _writing_ --tempfile is opened for reading, so don't try to empty it
  205.  * if you're using ftruncate.   Return -1 on error, 0 on success.
  206.  */
  207. emptyfile(fp, fname)
  208. register FILE **fp;
  209. register char *fname;
  210. {
  211.     Debug("Emptying \"%s\"\n", fname);
  212. #ifndef SYSV
  213.     return ftruncate(fileno(*fp), 0L);
  214. #else
  215.     {
  216.     int omask = umask(077), ret;
  217.     (void) fclose(*fp);
  218.     if (!(*fp = fopen(fname, "w")))
  219.         ret = -1;
  220.     ret = 0;
  221.     (void) umask(omask);
  222.     return ret;
  223.     }
  224. #endif /* SYSV */
  225. }
  226.  
  227. /*
  228.  * Finds out how many file descriptors are opened.  Useful for making sure
  229.  * no files got opened in subprocedures which were not subsequently closed.
  230.  * If argc is 0, returns the number of available fds.
  231.  */
  232. nopenfiles(argc)
  233. {
  234. #ifdef MAXFILES
  235.     register int size = MAXFILES;
  236. #else
  237.     register int size = getdtablesize();
  238. #endif /* MAXFILES */
  239.     register int nfiles = 0, totalfiles = size;
  240.  
  241.     if (argc > 1)
  242.     return -1;
  243.  
  244.     if (argc == 1)
  245.     wprint("open file descriptors:");
  246.     while (--size >= 0)
  247.     if (fcntl(size, F_GETFL, 0) != -1) {
  248.         if (argc == 1)
  249.         wprint(" %d", size);
  250.         ++nfiles;
  251.     }
  252.     if (argc == 1) {
  253.     wprint("\n");
  254.     return 0;
  255.     }
  256.     return totalfiles - nfiles;
  257. }
  258.  
  259. /*
  260.  * Close all "extraneous" file descriptors; return the number closed
  261.  */
  262. closefileds (n)
  263. {
  264.     register int nfiles = 0;
  265. #ifdef MAXFILES
  266.     register int size = MAXFILES;
  267. #else
  268.     register int size = getdtablesize();
  269. #endif /* MAXFILES */
  270.  
  271.     while (--size >= n)
  272.     if (fcntl(size, F_GETFL, 0) != -1) {
  273.         (void) close(size);
  274.         ++nfiles;
  275.     }
  276.     return nfiles;
  277. }
  278.  
  279. /*
  280.  * Open a path for writing or appending -- return a FILE pointer.
  281.  * If program is TRUE, then use popen, not fopen and don't check 
  282.  * to see if the file is writable.  If program is FALSE and lockit
  283.  * is TRUE, then lock on open.
  284.  */
  285. FILE *
  286. open_file(p, program, lockit)
  287. register char *p;
  288. {
  289.     register FILE *newfile = NULL_FILE;
  290.     register char *tmp;
  291.     int x = 1;
  292.  
  293.     if (program || *p == '/')
  294.     tmp = p, x = 0;
  295.     else
  296.     tmp = getpath(p, &x);
  297.     if (x == 1)
  298.     print("%s is a directory.\n", tmp);
  299.     else if (x == -1)
  300.     print("%s: %s\n", p, tmp);
  301.     else {
  302.     register char *mode = NULL;
  303.     /* if it doesn't exist open for "w" */
  304.     if (program || Access(tmp, F_OK))
  305.         mode = "w";
  306.     /* if we can't write to it, forget it */
  307.     else if (Access(tmp, W_OK))
  308.         error(tmp);
  309.     else
  310.         mode = "a";
  311.     if (mode)
  312.         if (program) {
  313.         if (!(newfile = popen(tmp, mode)))
  314.             error("Can't execute %s\n", tmp);
  315.         } else if (lockit) {
  316.         /* Lock on open */
  317.         if (!(newfile = lock_fopen(tmp, mode)))
  318.             error("Can't write to %s", tmp);
  319.         } else {
  320.         /* Ordinary open */
  321.         if (!(newfile = mask_fopen(tmp, mode)))
  322.             error("Can't write to %s", tmp);
  323.         }
  324.         if (newfile != NULL_FILE)
  325.         Debug("Successfully opened %s\n", tmp);
  326.     }
  327.     return newfile;
  328. }
  329.  
  330. /*
  331.  * Open each file in the vector names[] and place the corresponding
  332.  * file descriptor in files[].  If the file is really a program (pipe),
  333.  * delete the name after opening; otherwise lock the file.
  334.  * Tokens beginning with a "/, ~, or + are files; tokens beginning
  335.  * with a | are programs.
  336.  */
  337. open_list(names, files, size)
  338. char *names[];
  339. FILE *files[];
  340. {
  341.     register int total = 0, prog;
  342.     register char *fpath;
  343.  
  344.     Debug("opening "), print_argv(names);
  345.     for (total = 0; size && total < size; ) {
  346.     fpath = names[total] + (prog = (names[total][0] == '|'));
  347.     /* open_file() locks the file here only if prog is false */
  348.     if ((files[total] = open_file(fpath, prog, TRUE))) {
  349.         if (prog) {
  350.         xfree(names[total]);
  351.         names[total++] = NULL;
  352.         } else {
  353.         /* Seek to end of file AFTER locking */
  354.         (void) fseek(files[total++], 0L, 2);
  355.         }
  356.     } else {
  357.         Debug("Failed to open %s\n", names[total]);
  358.         /* Swap the failed file with the last in the list */
  359.         if (size--) {
  360.         xfree(names[total]);
  361.         names[total] = names[size];
  362.         names[size] = NULL;
  363.         }
  364.     }
  365.     }
  366.     return size;
  367. }
  368.  
  369. /*
  370.  * find_files gets a set of addresses and an array of
  371.  * char pointers and the maximum size that array can be.
  372.  * The object is to find the files or programs listed in "s".  If the
  373.  * size is 0, then just extract the file names and give error messages
  374.  * for each one since they will not be opened. Return the number of
  375.  * files found and delete all files from the list in * "s".
  376.  * The string "s" is modified to be a list of address -- all names AND
  377.  * files are stripped out of the list.
  378.  * The force parameter causes names to be interpreted as files even if
  379.  * they would normally appear to be addresses.
  380.  */
  381. find_files(s, names, size, force)
  382. register char *s;
  383. char *names[];
  384. {
  385.     register int     total = 0;
  386.     char          file[MAXPATHLEN], buf[HDRSIZ], *start = s, c;
  387.     register char    *p, *b = buf, *fpath;
  388.  
  389.     do  {
  390.     if (!(p = get_name_n_addr(s, NULL, file)))
  391.         break;
  392.     c = *p, *p = 0;
  393.     /* See if it's a file.  This doesn't get written back
  394.      * onto "buf" since it is supposed to be extracted anyway.
  395.      */
  396.     if (force || *file == '+' || *file == '~' ||
  397.         *file == '|' || *file == '/') {
  398.         int isdir;
  399.         /* open either "file" or &file[1] */
  400.         if (*file == '|') {
  401.         isdir = 0;
  402.         fpath = file;
  403.         } else {
  404.         isdir = 1;
  405.         /* if successful, getpath will reset isdir to 0 */
  406.         fpath = getpath(file, &isdir);
  407.         }
  408.         if (!isdir) {
  409.         if (size && total < size)
  410.             names[total++] = savestr(fpath);
  411.         else
  412.             print("No open space for %s\n", file);
  413.         } else if (isdir == 1)
  414.         print("%s: is a directory\n", file);
  415.         else
  416.         print("%s: %s\n", file, fpath);
  417.     } else {
  418.         b += Strcpy(b, s);
  419.         *b++ = ',', *b++ = ' ';
  420.     }
  421.     for (*p = c, s = p; *s == ',' || isspace(*s); s++)
  422.         ;
  423.     } while (*s);
  424.     for (*b-- = 0; b > buf && (*b == ',' || isspace(*b)); b--)
  425.     *b = 0;
  426.     (void) strcpy(start, buf);
  427.     names[total] = NULL; /* for free_vec() */
  428.     return total;
  429. }
  430.  
  431. /*
  432.  * access(2) has an undocumented feature which ignores suid.  If you are
  433.  * su'ed and try to read your mail, you will be unable to because access()
  434.  * will give the illusion that you cannot read/write to your mbox.  Solve
  435.  * the problem by using stat() instead.
  436.  */
  437. Access(file, mode)
  438. register char *file;
  439. {
  440.     struct stat buf;
  441.  
  442.     if (stat(file, &buf) == -1)
  443.     return -1;
  444.     if (mode == R_OK)
  445.     return (buf.st_mode & 0400)? 0 : -1;
  446.     if (mode == W_OK)
  447.     return (buf.st_mode & 0200)? 0 : -1;
  448.     return 0;
  449. }
  450.  
  451. /*
  452.  * Open a file for read/write/whatever but make sure umask is rw by user only.
  453.  */
  454. FILE *
  455. mask_fopen(file, mode)
  456. char *file, *mode;
  457. {
  458.     int omask = umask(077);
  459.     FILE *fp = fopen(file, mode);
  460.     (void) umask(omask);
  461.     return fp;
  462. }
  463.  
  464. /*
  465.  * Shorten a file name, replacing its full path name with one using an
  466.  *  accepted mush abbreviation:
  467.  *    ~    home directory
  468.  *    +    folder directory
  469.  *  For files in the current directory, the path is simply skipped.
  470.  * Returns a pointer into a static buffer holding the trimmed path.
  471.  */
  472. char *
  473. trim_filename(name)
  474. char *name;
  475. {
  476.     static char buf[MAXPATHLEN];
  477.     char *fldr = do_set(set_options, "folder"),
  478.      *home = do_set(set_options, "home");
  479.     int len;
  480.  
  481.     /* Handling $folder is tough, because if it is not set then we should
  482.      * trim DEF_FOLDER; but DEF_FOLDER may not be a full path, and we can't
  483.      * call getpath() because the "name" parameter may point to gepath()'s
  484.      * static buffer.  So we handle the special case of DEF_FOLDER starting
  485.      * with a tilde ($home), and forget about it otherwise.  Yuck.
  486.      */
  487.     if ((!fldr || !*fldr) && (fldr = DEF_FOLDER) && *fldr == '~' && home) {
  488.     (void) sprintf(buf, "%s%s", home, fldr + 1);
  489.     fldr = buf;  /* buf will get overwritten again below */
  490.     }
  491.     /* One more special case: if $folder and $home are the same, then we
  492.      * trim as $home, otherwise we trim as $folder.  This prevents strange
  493.      * contractions like "+.cshrc" for "~/.cshrc".
  494.      */
  495.     if ((!home || strcmp(home, fldr)) && (len = strlen(fldr)) &&
  496.         !strncmp(fldr, name, len) && (name[len] == '/' || !name[len])) {
  497.     buf[0] = '+';
  498.     if (name[len] && name[len + 1])
  499.         (void) strcpy(buf + 1, name + len + 1);
  500.     else
  501.         buf[1] = 0;
  502.     return buf;
  503.     } else if (home && (len = strlen(home)) && !strncmp(home, name, len) &&
  504.         (name[len] == '/' || !name[len])) {
  505.     buf[0] = '~';
  506.     (void) strcpy(buf + 1, name + len);
  507.     return buf;
  508.     } else if ((fldr = do_set(set_options, "cwd")) &&
  509.         (len = strlen(fldr)) && !strncmp(fldr, name, len) &&
  510.         name[len] == '/')
  511.     return strcpy(buf, name + len + 1);
  512.     return strcpy(buf, name);
  513. }
  514.